﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Newtonsoft.Json;

namespace mrp16062010.Simulation
{
    [JsonObject(IsReference = true)]
    class Makine
    {
        public Makine()
        {
            kuyrukGiris = new Kuyruk();
            kuyrukGiris.GirisMiCikisMi = Kuyruk.GirisCikis.Giris;
            kuyrukGiris.AitMakine = this;
            kuyrukGiris.Eklendi += new Kuyruk.EklendiHandler(kuyrukGiris_Eklendi);
            kuyrukCikis = new Kuyruk();
            kuyrukGiris.GirisMiCikisMi = Kuyruk.GirisCikis.Cikis;
            kuyrukCikis.AitMakine = this;
            kuyrukCikis.Eklendi += new Kuyruk.EklendiHandler(kuyrukCikis_Eklendi);
            kuyrukGiris.Ekleniyor += new Kuyruk.EkleniyorHandler(kuyrukGiris_Ekleniyor);
            tamamlanmamis = new List<IsParcasi>();
            tatilList = new List<IsParcasi>();
            tarihce = new List<IsParcasi>();
        }

        

        void kuyrukGiris_Ekleniyor(IsParcasi o, EventArgs e)
        {

        }

        void kuyrukCikis_Eklendi(IsParcasi o, EventArgs e)
        {

        }

        void kuyrukGiris_Eklendi(IsParcasi o, EventArgs e)
        {

        }

        public int Kod { get; set; }
        public int Id { get; set; }
        public string Ad { get; set; }

        public IsMerkezi Merkez { get; set; }

        private DateTime sonTarih;

        public DateTime SonTarih
        {
            get { return sonTarih; }
            set { sonTarih = value; }
        }

        List<IsParcasi> tarihce;

        public List<IsParcasi> Tarihce
        {
            get
            {
                return tarihce;
            }
        }

        public Ortam BulunduguOrtam { get; set; }

        private Kuyruk kuyrukGiris;

        public Kuyruk KuyrukGiris
        {
            get { return kuyrukGiris; }
            set { kuyrukGiris = value; }
        }

        private Kuyruk kuyrukCikis;

        public Kuyruk KuyrukCikis
        {
            get { return kuyrukCikis; }
            set { kuyrukCikis = value; }
        }

        private List<IsParcasi> tamamlanmamis;

        public List<IsParcasi> Tamamlanmamis
        {
            get { return tamamlanmamis; }
            set { tamamlanmamis = value; }
        }


        private List<IsParcasi> tatilList;

        public List<IsParcasi> TatilList
        {
            get { return tatilList; }
            set { tatilList = value; }
        }

        private TimeSpan calismaSuresi;

        public TimeSpan CalismaSuresi
        {
            get { return calismaSuresi; }
            set { calismaSuresi = value; }
        }

        private IsParcasi islemdekiParca;

        public IsParcasi IslemdekiParca
        {
            get { return islemdekiParca; }
            set { islemdekiParca = value; }
        }

        private int makinedekiParcalarSayisi;

        public int MakinedekiParcalarSayisi
        {
            get
            {
                makinedekiParcalarSayisi = 0;
                if (sonIslenen != null)
                {
                    if (sonIslenen.SimulasyonBilgisi.IslemeTarihi.BitisTarihi > BulunduguOrtam.SonTarih)
                    {
                        TimeSpan kalanZaman = sonIslenen.Uzunluk - sonIslenen.Islenen;
                        int kalanadet = Convert.ToInt32(Math.Ceiling(kalanZaman.TotalMinutes * (Convert.ToDouble(sonIslenen.ParcaAdedi) / Convert.ToDouble(sonIslenen.Uzunluk.TotalMinutes))));
                        makinedekiParcalarSayisi += kalanadet;
                    }
                }
                makinedekiParcalarSayisi += kuyrukGiris.KalanParcaAdedi;
                return makinedekiParcalarSayisi;
            }
        }

        private TimeSpan kalanUzunluk;

        public TimeSpan KalanUzunluk
        {
            get
            {
                kalanUzunluk = new TimeSpan();
                kalanUzunluk += kuyrukGiris.KalanUzunluk;
                if (sonIslenen != null)
                {
                    if (sonIslenen.SimulasyonBilgisi.IslemeTarihi.BitisTarihi > BulunduguOrtam.SonTarih)
                    {
                        TimeSpan kalanZaman = sonIslenen.Uzunluk - sonIslenen.Islenen;
                        kalanUzunluk += kalanZaman;
                    }
                }
                return kalanUzunluk;
            }
        }

        private TimeSpan islemdekiKalanUzunluk;

        public TimeSpan IslemdekiKalanUzunluk
        {
            get
            {
                islemdekiKalanUzunluk = new TimeSpan();
                if (sonIslenen != null)
                {
                    if (sonIslenen.SimulasyonBilgisi.IslemeTarihi.BitisTarihi > BulunduguOrtam.SonTarih)
                    {
                        TimeSpan kalanZaman = sonIslenen.Uzunluk - sonIslenen.Islenen;
                        islemdekiKalanUzunluk = kalanZaman;
                    }
                }
                return islemdekiKalanUzunluk;
            }
            
        }

        private int islemdekiKalanAdet;

        public int IslemdekiKalanAdet
        {
            get
            {
                islemdekiKalanAdet = 0;
                if (sonIslenen != null)
                {
                    if (sonIslenen.SimulasyonBilgisi.IslemeTarihi.BitisTarihi > BulunduguOrtam.SonTarih)
                    {
                        TimeSpan kalanZaman = sonIslenen.Uzunluk - sonIslenen.Islenen;
                        int kalanadet = Convert.ToInt32(Math.Ceiling(kalanZaman.TotalMinutes * (Convert.ToDouble(sonIslenen.ParcaAdedi) / Convert.ToDouble(sonIslenen.Uzunluk.TotalMinutes))));
                        islemdekiKalanAdet = kalanadet;
                    }
                }
                return islemdekiKalanAdet;
            }
        }
        
        

        public MakineDurumu Durum { get; set; }

        public enum MakineDurumu
        {
            Boş = 0, Dolu = 1
        }

        public delegate void GirerkenHandler(object o, EventArgs e);

        public event GirerkenHandler Girerken;

        protected virtual void OnGirerken(object o, EventArgs e)
        {
            if (Girerken != null)
            {
                Girerken(o, e);
            }
        }

        public delegate void IsleniyorHandler(object o, EventArgs e);

        public event IsleniyorHandler Isleniyor;

        protected virtual void OnIsleniyor(object o, EventArgs e)
        {
            if (Isleniyor != null)
            {
                Isleniyor(o, e);
            }
        }

        public delegate void IslendiHandler(object o, EventArgs e);

        public event IslendiHandler Islendi;

        protected virtual void OnIslendi(object o, EventArgs e)
        {
            if (Islendi != null)
            {
                Islendi(o, e);
            }
        }

        public delegate void CiktiHandler(object o, IsParcasi cikan, EventArgs e);

        public event CiktiHandler Cikti;

        protected virtual void OnCikti(object o, IsParcasi cikan, EventArgs e)
        {
            if (Cikti != null)
            {
                Cikti(o, cikan, e);
            }
        }

        private List<GelecekBilgisi> gelecekListe = new List<GelecekBilgisi>();

        private void GelecekListeEkle(DateTime tarih,IsParcasi eklenecek)
        {
            GelecekBilgisi bilgi = new GelecekBilgisi(tarih,eklenecek);

            gelecekListe.Add(bilgi);
            gelecekListe = gelecekListe.OrderBy(x => x.TarihBaslanacak).ToList();

        }

        public void IlkEkleme(List<IsParcasi> liste)
        {
            foreach (IsParcasi parca in liste)
            {
                Log.Ekle(parca.Op.Ad + " İlk ekleme " + parca.Uzunluk + " MakNo:" + this.Kod);
                parca.SimulasyonBilgisi.GirisBeklemeTarihi.BaslangicTarihi = sonTarih;
                this.KuyrukGiris.Ekle(parca);
            }
        }

        public void Tetikle(DateTime gelenTarih)
        {
            if (gelenTarih.TatilMi() == true)
            {
                return;
            }

            if (sonIslenen!=null)
            {
                sonIslenen.Islenen = sonIslenen.Islenen.Add(new TimeSpan(0, Convert.ToInt32(Ayarlar.Nesne.SimilasyonCalismaAraligi.TotalMinutes), 0));
                if (gelenTarih==sonIslenen.SimulasyonBilgisi.IslemeTarihi.BitisTarihi)
                {
                    //parça işlenmiş demektir
                    if (sonIslenen.OnundekiIsParcasi==null)
                    {
                        //son parça demektir.
                        this.BulunduguOrtam.IslenmekteOlanSiparisler.Remove(sonIslenen.SiparisBilgi);
                        //son parça bitti ortamın işlenmekte olanından çıkar.
                    }
                }
            }

            //Console.Out.WriteLine("Tetiklendi: " + this.Kod.ToString() + " Gelen tarih:" + gelenTarih.ToString());
            
            DegisikliklereBak();

            if (gelenTarih >= sonTarih)
            {
                if (kuyrukGiris.Liste.Count == 0)
                {
                    sonTarih = gelenTarih;
                    Isle();
                }
                else
                {
                    Isle();
                }
            }
        }

        bool degisiklikKontroldeMi = false;

        private void DegisikliklereBak()
        {
            lock (this)
            {
                List<GelecekBilgisi> alinacakList = gelecekListe.Where(x => x.TarihBaslanacak <= sonTarih).ToList();

                if (alinacakList.Count > 0)
                {
                    degisiklikKontroldeMi = true;
                    foreach (GelecekBilgisi item in alinacakList)
                    {

                        gelecekListe.Remove(item);
                        item.Parca.SimulasyonBilgisi.GirisBeklemeTarihi.BaslangicTarihi = sonTarih;
                        kuyrukGiris.Ekle(item.Parca);
                        Isle();
                        //Ekle(item, item.ArkasindakiIsParcalari.FirstOrDefault());
                    }
                    degisiklikKontroldeMi = false;
                }
            }
        }

        

        public void Ekle(IsParcasi parca, IsParcasi gonderenParca)
        {

            if (this.Girerken != null)
            {
                this.Girerken(this, new EventArgs()); 
            }

            if (!tarihce.Contains(parca))
            {
                tarihce.Add(parca);
            }

            Log.Ekle("Kuyruğa eklenecek: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:"+parca.Uzunluk+" SIPNo:"+ parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
            
            if (!degisiklikKontroldeMi)
            { DegisikliklereBak(); }

            if (sonTarih == DateTime.MinValue)
            {
                sonTarih = BulunduguOrtam.BaslangicTarihi;
            }

            parca.parcaBitti(gonderenParca);
            if (parca.HazirMi)
            {
                if (sonTarih < gonderenParca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi)
                {
                    //eğer ki gönderen parçanın işleme bitiş tarihi şu andan sonra ise gelecek listeye ekle
                    Log.Ekle("Gelecege eklendi: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
                    GelecekListeEkle(gonderenParca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi, parca);
                }
                else
                {
                    //eğer ki gönderen parçanın işleme bitiş tarihi şu andan önce ise veya eşit ise
                    //direk kuyruğa at
                    //TODO baslangc tarrhn doluysa guncelleme
                    Log.Ekle("Kuyruga eklendI: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
                    parca.SimulasyonBilgisi.GirisBeklemeTarihi.BaslangicTarihi = sonTarih;
                    kuyrukGiris.Ekle(parca);
                }
            }
            else
            {
                //Parça başka parçaların bitmesini bekliyor, yani tamamlanmamış
                Log.Ekle("EklenemedI beklIyor: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
            }

        }

        private DateTime Yerlestirme(IsParcasi parca, DateTime tarih, TimeSpan kalan)
        {

            if (tarih + kalan > tarih.MesaiBitis() && tarih.TatilMi() == false)
            {
                //Mesai dışına taşıyor
                TimeSpan islenmesiGereken = kalan - (tarih.MesaiBitis() - tarih);

                return Yerlestirme(parca, tarih.AddDays(1).MesaiBaslangic(), islenmesiGereken);
            }
            else if (tarih + kalan <= tarih.MesaiBitis() && tarih.TatilMi() == false)
            {
                //Mesai içinde bitiyor
                return tarih + kalan;
            }
            else
            {
                return Yerlestirme(parca, tarih.AddDays(2).MesaiBaslangic(), kalan);
            }
        }

        IsParcasi sonIslenen = null;
        private void Isle()
        {
            lock (this)
            {
                if (Durum == MakineDurumu.Boş)
                {
                    //Kuyruktan iş parçası çekelim
                    IsParcasi parca;

                    parca = this.KuyrukGiris.Cek();


                    if (parca != null)
                    {
                        islemdekiParca = parca;
                        //parçayı planlama çizelgesinden siliyoruz
                        parca.CizelgedenCikar();
                        Log.Ekle("IslenIyor: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
                        //Console.Out.WriteLine("İşleniyor: "+this.Kod.ToString() +" "+ sonTarih.ToString() + " " + parca.SiparisBilgi.SiparisNo + " " + parca.Op.Ad + " " + parca.OlusanStok.Ad);
                        Durum = MakineDurumu.Dolu;
                        if (this.Isleniyor != null)
                        { this.Isleniyor(this, new EventArgs()); }
                        #region islem
                        parca.SimulasyonBilgisi.GirisBeklemeTarihi.BitisTarihi = this.sonTarih;
                        parca.SimulasyonBilgisi.IslemeTarihi.BaslangicTarihi = this.sonTarih;
                       

                        this.sonTarih = Yerlestirme(parca, this.sonTarih, parca.Uzunluk);
                        
                        //sonTarih = parca.BasTarihi;
                        //İsleme yap
                        //if (tatil != null)
                        //{
                        //    this.sonTarih = tatil.BitTarihi;
                        //    parca.SimulasyonBilgisi.Tatil = tatil;
                        //}
                        
                        //parca.SimulasyonTarihi.BaslangicTarihi = this.sonTarih;

                        //Tatil bitiş ile başlangıç arasındaysa bitişi ertele
                        //tatil = null;
                        //tatil = tatilList.Where(p => p.BasTarihi > this.sonTarih && p.BitTarihi < this.sonTarih + parca.Uzunluk).FirstOrDefault();
                        //if (tatil != null)
                        //{
                        //    parca.SimulasyonBilgisi.Tatil = tatil;
                        //    this.sonTarih += tatil.BitTarihi - tatil.BasTarihi;
                        //}
                        parca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi = this.sonTarih;
                        calismaSuresi = calismaSuresi.Add(parca.Uzunluk);
                        //Kuyruğa at
                        Log.Ekle("IslendI: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
                        //Console.Out.WriteLine("İşlendi: " + this.Kod.ToString() + " " + sonTarih.ToString() + " " + parca.SiparisBilgi.SiparisNo + " " + parca.Op.Ad + " " + parca.OlusanStok.Ad);
                        #endregion islem
                        sonIslenen = parca;
                        Durum = MakineDurumu.Boş;
                        islemdekiParca = null;
                        if (this.Islendi != null)
                        { this.Islendi(this, new EventArgs()); }
                        
                        Cikar(parca);
                        /*
                        if (kuyrukGiris.Liste.Where(x=>x.HazirMi==true).Count()>0)
                        {
                            ////Isle();
                        }*/
                    }
                }



            }
        }

        private void Cikar(IsParcasi parca)
        {
            if (parca.Uzunluk.TotalSeconds==3312&&this.Kod==16)
            {
                
            }
            Log.Ekle("CIKARILIYOR: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
            //Console.Out.WriteLine("Çıkarılıyor: " + this.Kod.ToString() + " " + sonTarih.ToString() + " " + parca.SiparisBilgi.SiparisNo + " " + parca.Op.Ad + " " + parca.OlusanStok.Ad);

            kuyrukCikis.Ekle(parca);
            parca.SimulasyonBilgisi.CikisBeklemeTarihi.BaslangicTarihi = parca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi;
            Log.Ekle("CIKARILDI: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
            //Console.Out.WriteLine("Çıkarıldı: " + this.Kod.ToString() + " " + sonTarih.ToString() + " " + parca.SiparisBilgi.SiparisNo + " " + parca.Op.Ad + " " + parca.OlusanStok.Ad);
            //sonraki parçayı ilgili makineye gönderelim
            if (parca.OnundekiIsParcasi != null)
            {
                Log.Ekle("SONRAKI GONDER: MakNo:" + parca.OnundekiIsParcasi.IM.Kod + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.OnundekiIsParcasi.Uzunluk + " SIPNo:" + parca.OnundekiIsParcasi.SiparisBilgi.SiparisNo + " OpAd:" + parca.OnundekiIsParcasi.Op.Ad + " OlStokAd:" + parca.OnundekiIsParcasi.OlusanStok.Ad+" KAC PARCA:"+parca.OnundekiIsParcasi.ArkasindakiIsParcalari.Count.ToString());
                //Console.Out.WriteLine("Sonraki parça gönderiliyor: " + this.Kod.ToString() + " " + sonTarih.ToString() + " " + parca.SiparisBilgi.SiparisNo + " " + parca.Op.Ad + " " + parca.OlusanStok.Ad);
                parca.SimulasyonBilgisi.CikisBeklemeTarihi.BitisTarihi = parca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi;
                BulunduguOrtam.ParcaAta(parca.OnundekiIsParcasi, parca);
                kuyrukGiris.Liste.Remove(parca);
                
                kuyrukCikis.Liste.Remove(parca);
            }
            else
            {
                //Son parça
                //Parçanın siparişine ürünün hazır olduğu tarihi güncelliyoruz
                Log.Ekle("URUN OLUSTU: MakNo:" + this.Kod.ToString() + " " + sonTarih.ToString() + " " + "Uzunluk:" + parca.Uzunluk + " SIPNo:" + parca.SiparisBilgi.SiparisNo + " OpAd:" + parca.Op.Ad + " OlStokAd:" + parca.OlusanStok.Ad);
                parca.SiparisBilgi.SimilasyonTarihleri.BitisTarihi = parca.SimulasyonBilgisi.IslemeTarihi.BitisTarihi;
                
            }

            if (this.Cikti != null)
            {
                this.Cikti(this, parca, new EventArgs()); 
            }
            //DegisikliklereBak();
            //GC.Collect();
        }
        //Sadece planlama zamanında ilk parçalar gelirse t anında buradan çeker
        
    }
}
